/*
 * Package il.ac.biu.cs.grossmm.impl.activeData
 * File ConversionChain.java
 * 
 * This is a part of presence service framework prototype. 
 * See javadoc for more information.
 *  
 * Copyright (C) 2006 Grossmann Mark
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package il.ac.biu.cs.grossmm.impl.activeData;

/**
 * Represents a conversion chain. A conversion chain is a sequence of converters
 * such that when they can sequentially applied thus comprising a single
 * combined converter from one resource state type to another.
 * 
 */
public class ConversionChain {
	private final ConversionProcessor[] chain;

	private final double priority;

	private final boolean isConditional;

	private static final ConversionProcessor[] EMPTY = new ConversionProcessor[0];

	/**
	 * Creates a new unconditional conversion chain based on an array of
	 * converters and the priority
	 * 
	 * @param chain
	 *            arary of converters
	 * @param priority
	 *            chain priority
	 */
	public ConversionChain(ConversionProcessor[] chain, double priority) {
		this.chain = chain;
		this.priority = priority;
		this.isConditional = false;
	}

	/**
	 * Creates a new conditional conversin chain which is an existing base
	 * conversion chain with a new converter appended at the end. The base
	 * conversion chain is not changed.
	 * 
	 * @param baseChain
	 *            the base conversion chain, null is equivalent to the empty
	 *            chain
	 * @param convertor
	 *            the converter to append at the end of the base conversion
	 *            chain
	 */
	public ConversionChain(ConversionChain baseChain,
			ConversionProcessor convertor) {
		if (baseChain == null || baseChain.chain.length == 0) {
			// base chain is empty.
			// create a chain consisting of a single converter
			this.chain = new ConversionProcessor[] { convertor };
			this.priority = convertor.getPriority();
		} else {
			// use fast array-copy operation to append the clone the existing
			// chain and append the new converter at the end

			ConversionProcessor[] chain0 = baseChain.chain;

			int len = chain0.length;
			this.chain = new ConversionProcessor[len + 1];
			System.arraycopy(baseChain, 0, this.chain, 0, len);
			this.chain[len] = convertor;

			// compute and set the priority
			this.priority = baseChain.priority * convertor.getPriority();
		}

		this.isConditional = true;
	}

	/**
	 * Creates a new conversion chain which is a concatenatioh of two conversion
	 * chains given as parameters
	 * 
	 * @param leftChain
	 *            the first conversion chain
	 * @param rightChain
	 *            the second conversion chain
	 */
	public ConversionChain(ConversionChain leftChain, ConversionChain rightChain) {
		ConversionProcessor[] chain0 = leftChain == null ? leftChain.chain
				: EMPTY;
		ConversionProcessor[] chain1 = rightChain.chain;

		// use fast array-copy operation to perform the concatenation

		int len = chain0.length + chain1.length;
		this.chain = new ConversionProcessor[len];
		System.arraycopy(chain0, 0, this.chain, 0, chain0.length);
		System.arraycopy(chain1, 0, this.chain, chain0.length, chain1.length);

		// compute and set the priority
		this.priority = leftChain.priority * rightChain.priority;

		// the new chain is confitional if at least one of the given chains is
		// conditional
		this.isConditional = leftChain != null && leftChain.isConditional;
	}

	/**
	 * Gets the priority of this conversion chain
	 * @return the priority of this conversion chain
	 */
	public double getPriority() {
		return priority;
	}

	/**
	 * Gets the converters comprising this conversion chain as an array.
	 * This array should not be externally modified.
	 * @return the converters comprising this conversion chain as an array
	 */
	public ConversionProcessor[] getConverters() {
		return chain;
	}

	/**
	 * Checks whether this conversion chain is a conditional chain
	 * @return true iff this conversion chain is conditional
	 */
	public boolean isConditional() {
		return isConditional;
	}

}
